package com.hgsoft.yfzx.component.runninglog.component.log;

import org.apache.logging.log4j.core.Filter;
import org.apache.logging.log4j.core.Layout;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.appender.AbstractAppender;
import org.apache.logging.log4j.core.config.plugins.Plugin;
import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
import org.apache.logging.log4j.core.config.plugins.PluginElement;
import org.apache.logging.log4j.core.config.plugins.PluginFactory;
import org.apache.logging.log4j.core.impl.ThrowableProxy;
import org.apache.logging.log4j.core.layout.PatternLayout;

import java.io.Serializable;
import java.text.DateFormat;
import java.util.Date;

/**
 * 自定义Appender,使用WebSocket将日志信息输出到界面中
 * <p>
 * 为了保证该类的移植性,尽可能使用便于移植的方法，即减少对其他jar包的依赖
 * </p>
 *
 * @author Sawyerlsy
 * @date 2018-12-29
 * @since
 */
@Plugin(name = "WebSocket", category = "Core", elementType = "appender", printObject = true)
public class WebSocketAppender extends AbstractAppender {

    /**
     * 字符编码
     */
    private String charset;

    /**
     * 默认字符编码为UTF-8
     */
    private static final String DEFAULT_CHARSET = "UTF-8";

    /**
     * 基础框架放置在日志上下文中用户名对应的key值
     */
    private static final String CONTEXT_USERNAME_KEY = "userId";

    /**
     * 基础框架放置在日志上下文中ip地址对应的key值
     */
    private static final String CONTEXT_CLIENTIP_KEY = "clientIP";

    protected WebSocketAppender(String name, Filter filter, Layout<? extends Serializable> layout,
                                boolean ignoreExceptions, String charset) {
        super(name, filter, layout, ignoreExceptions);
        this.charset = charset;
    }

    @Override
    public void append(LogEvent event) {
        //将日志放入队列中
        LoggerQueue.getInstance().push(build(event));
    }

    /**
     * 接收配置文件中的参数信息,如果有需要可以自行拓展
     */
    @PluginFactory
    public static WebSocketAppender createAppender(@PluginAttribute("name") String name,
                                                   @PluginAttribute("charset") String charset,
                                                   @PluginElement("Filter") final Filter filter,
                                                   @PluginElement("Layout") Layout<? extends Serializable> layout,
                                                   @PluginAttribute("ignoreExceptions") boolean ignoreExceptions) {
        if (name == null) {
            LOGGER.error("No name provided for WebSocketAppender");
            return null;
        }

        if (charset == null) {
            charset = DEFAULT_CHARSET;
        }

        if (layout == null) {
            layout = PatternLayout.createDefaultLayout();
        }

        return new WebSocketAppender(name, filter, layout, ignoreExceptions, charset);
    }

    /**
     * 根据LogEvent构建日志对象LogMessage
     *
     * @param event 日志事件,包含打印信息
     * @return LogMessage
     */
    private LogMessage build(LogEvent event) {
        //构建基础日志信息
        LogMessage logMessage = LogMessage.bulid()
                .setLogTime(DateFormat.getDateTimeInstance().format(new Date(event.getTimeMillis())))
                .setClassName(event.getLoggerName())
                .setThreadName(event.getThreadName())
                .setLevel(event.getLevel().name())
                .setMessage(event.getMessage().getFormattedMessage());

        if (null != event.getSource()) {
            logMessage.setMethodName(event.getSource().getMethodName())
                    .setLineNumber(String.valueOf(event.getSource().getLineNumber()));
        }

        //如果有拓展的需要可以将日志原内容输出,这里为了减少传输的字节数,进行屏蔽
        //logMessage.setSource(new String(getLayout().toByteArray(event), charset));

        //获取基础框架放置在日志上下文中的userId、clientIp,正常情况下这两个属性不会反生变动,如果发生变更需要进行修改
        String username = event.getContextMap().get(CONTEXT_USERNAME_KEY);
        String clientIp = event.getContextMap().get(CONTEXT_CLIENTIP_KEY);
        logMessage.setUsername(null == username ? "anonymous" : username)
                .setClientIp(null == clientIp ? "x.x.x.x" : clientIp);

        //获取异常信息
        ThrowableProxy throwableProxy = event.getThrownProxy();
        if (throwableProxy != null) {
            StringBuilder exceptionStringBuilder = new StringBuilder();
            exceptionStringBuilder.append("<span class='stackTrace-message'>")
                    .append(throwableProxy.getMessage())
                    .append("</span>");

            StackTraceElement[] stackTraceElementArray = throwableProxy.getStackTrace();
            for (StackTraceElement stackTraceElement : stackTraceElementArray) {
                exceptionStringBuilder.append("<span class='stackTrace-item'>")
                        .append(String.valueOf(stackTraceElement))
                        .append("</span>");
            }
            logMessage.setExceptionStr(exceptionStringBuilder.toString());
        }

        return logMessage;
    }
}
